#pragma once
/*
一. 这里编写一个文件工具类,以便后续开发的便利性
主要提供以下方法:
文件属性有关:
1. 获取文件大小
2. 获取文件的最近修改时间(M)
3. 获取文件的最近访问时间(A)
4. 获取文件名
输入输出有关:
1. 定点定长读取;
2. 向文件中进行写入;
3. 从文件中读取数据;
压缩/解压相关:
1. 压缩接口;
2. 解压接口;
其它:
1. 判断一个文件是否存在;
2. 创建目录
3. 获取当前文件的父目录下的所有普通文件;

*/

/*
二. 这里再开发一个Json的序列化和反序列化工具类
1. 序列化接口
2. 反序列化接口
*/
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <experimental/filesystem>
#include <memory>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <jsoncpp/json/json.h>
#include "./lib/bundle.h"
namespace Cloud
{
    namespace fs = std::experimental::filesystem;
    class fileUtil
    {
    public:
        fileUtil(const std::string &filename);
        // 获取文件大小
        int64_t fileSize();
        // 获取文件的最近修改时间
        time_t getModifyTime();
        // 获取文件的最近访问时间
        time_t getAccessTime();
        // 获取文件名
        std::string filename();
        // 定点定长读取
        bool getPosLen(std::string *body, size_t pos, size_t len);
        // 获取文件中的全部信息
        bool getContent(std::string *body);
        // 向文件中写入信息;
        bool setContent(const std::string &body);
        // 压缩文件
        bool compress(const std::string destFielname);
        // 解压文件
        bool unCompress(const std::string destFielname);
        // 判断当前文件是否存在
        bool exists();
        // 创建目录
        bool createDirectory();
        // 查找当前文件父目录下的所有普通文件
        bool scanDirectory(std::vector<std::string> *vs);

    private:
        std::string _filename;
    };

    class JsonUtil
    {
    public:
        // 将结果序列化到输出流
        static bool serializeToOstream(const Json::Value &root, std::ostream *out);
        // 将结果序列化到字符串
        static bool serializeToString(const Json::Value &root, std::string *out);
        // 将结果反序列化到输入流
        static bool parseFromIstream(const std::istream &in, Json::Value *root);
        // 将结果反序列化到字符串
        static bool parseFromString(const std::string &in, Json::Value *root);
    };

    // 将结果序列化到输出流
    bool JsonUtil::serializeToOstream(const Json::Value &root, std::ostream *out)
    {
        Json::StreamWriterBuilder builder;
        std::shared_ptr<Json::StreamWriter> S1(builder.newStreamWriter());
        return S1->write(root, out);
    }
    // 将结果序列化到字符串
    bool JsonUtil::serializeToString(const Json::Value &root, std::string *out)
    {
        std::stringstream str;
        bool ret = serializeToOstream(root, &str);
        *out = str.str();
        return ret;
    }
    // 将结果反序列化到输入流
    bool JsonUtil::parseFromIstream(const std::istream &in, Json::Value *root)
    {
        std::string err;
        Json::CharReaderBuilder build;
        return Json::parseFromStream(build, const_cast<std::istream &>(in), root, &err);
    }
    // 将结果反序列化到字符串
    bool JsonUtil::parseFromString(const std::string &in, Json::Value *root)
    {
        std::stringstream str;
        str << in;
        return parseFromIstream(str, root);
    }

    fileUtil::fileUtil(const std::string &filename) : _filename(filename)
    {
    }
    int64_t fileUtil::fileSize()
    {
        struct stat st;
        if (stat(const_cast<char *>(_filename.c_str()), &st) < 0)
            return -1;
        return st.st_size;
    }
    time_t fileUtil::getModifyTime()
    {
        struct stat st;
        if (stat(const_cast<char *>(_filename.c_str()), &st) < 0)
            return -1;
        return st.st_mtim.tv_sec;
    }
    time_t fileUtil::getAccessTime()
    {
        struct stat st;
        if (stat(const_cast<char *>(_filename.c_str()), &st) < 0)
            return -1;
        return st.st_atim.tv_sec;
    }
    std::string fileUtil::filename()
    {
        if (_filename == "/")
            return _filename;
        int pos = _filename.find_last_of('/');
        if (pos == std::string::npos)
            return _filename;
        return _filename.substr(pos + 1);
    }
    bool fileUtil::getPosLen(std::string *body, size_t pos, size_t len)
    {
        int64_t size = fileSize();
        // 超出了能读取的范围
        if (size < 0)
            return false;
        if (pos + len > size)
            return false;
        std::fstream fin(_filename, std::fstream::in | std::fstream::binary);
        if (!fin)
            return false;
        fin.seekg(pos, fin.beg);
        body->resize(len);
        fin.read(const_cast<char *>(body->c_str()), len);
        return fin.good();
    }
    bool fileUtil::getContent(std::string *body)
    {
        // 本质就是定点获取(从0开始获取,size个数据)
        return getPosLen(body, 0, fileSize());
    }
    bool fileUtil::setContent(const std::string &body)
    {
        // 1.打开文件
        std::fstream fout(_filename, std::fstream::binary | std::fstream::out | std::fstream::trunc);
        if (!fout)
            return false;
        // 2. 写入内容
        fout.write(body.c_str(), body.size());
        return fout.good();
    }
    bool fileUtil::compress(const std::string destFielname)
    {
        // 1.获取文件数据
        std::string body;
        if (!getContent(&body))
            return false;
        // 2.压缩文件
        body = bundle::pack(bundle::LZ4, body);
        // 3. 将压缩数据进行写入
        fileUtil f2(destFielname);
        return f2.setContent(body);
    }
    bool fileUtil::unCompress(const std::string destFielname)
    {
        // 1.获取文件数据
        std::string body;
        if (!getContent(&body))
            return false;
        // 2. 将数据进行解压
        body = bundle::unpack(body);
        // 3. 写入解压数据
        fileUtil f1(destFielname);
        return f1.setContent(body);
    }
    bool fileUtil::exists()
    {
        fs::path str(_filename);
        return fs::exists(str);
    }
    bool fileUtil::createDirectory()
    {
        if (exists())
            return true;
        fs::path str(_filename);
        // 获取父目录
        str = str.parent_path();
        return fs::create_directories(str);
    }
    bool fileUtil::scanDirectory(std::vector<std::string> *ret)
    {
        if (!exists())
            return false;
        fs::path str(_filename);
        fs::directory_entry entry(str);
        // 构造一个目录迭代器
        fs::directory_iterator lst(str);
        for (auto &e : lst)
        {
            // 这不是一个目录
            if (e.status().type() != fs::file_type::directory)
                // 将e查入vector
                ret->push_back(e.path().string());
        }
        return true;
    }
}
